package cn.lingyangwl.agile.msg.push.service.impl;

import cn.lingyangwl.agile.model.constants.*;
import cn.lingyangwl.agile.msg.push.api.constatnts.*;
import cn.lingyangwl.agile.msg.push.api.model.content.*;
import cn.lingyangwl.agile.msg.push.api.model.rqrs.*;
import cn.lingyangwl.agile.msg.push.assembly.*;
import cn.lingyangwl.agile.msg.push.core.*;
import cn.lingyangwl.agile.msg.push.core.propcess.*;
import cn.lingyangwl.agile.msg.push.manager.*;
import cn.lingyangwl.agile.msg.push.model.entity.*;
import cn.lingyangwl.agile.msg.push.model.rqrs.history.*;
import cn.lingyangwl.agile.msg.push.service.*;
import cn.lingyangwl.agile.system.*;
import cn.lingyangwl.agile.system.model.rqrs.tenant.*;
import cn.lingyangwl.agile.tenant.core.*;
import cn.lingyangwl.framework.data.mate.annotations.*;
import cn.lingyangwl.framework.tool.core.exception.*;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 消息推送
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class MsgHistoryServiceImpl implements MsgHistoryService {

    @Resource
    private MsgHistoryManager historyManager;
    @Resource
    private MsgHistoryAssembly historyAssembly;
    @Resource
    private MsgPushProcess pushProcess;

    @Resource
    private MsgNoticeService noticeService;
    @Resource
    private NoticeChannelService channelService;
    @Resource
    private SystemApi systemApi;

    /**
     * 分页查询 消息推送
     *
     * @return 返回分页数据
     */
    @Override
    @FieldBindHandle
    public IPage<HistoryQueryResp> pageHistory(HistoryPageReq req) {
        IPage<MsgHistory> historyPage = historyManager.pageHistory(req);
        if (historyPage.getRecords().isEmpty()) {
            return new Page<>();
        }

        List<Long> noticeIds = historyPage.getRecords().stream().map(MsgHistory::getNoticeId).collect(Collectors.toList());
        List<String> tenantIds = historyPage.getRecords().stream().map(MsgHistory::getTenantId).collect(Collectors.toList());

        Map<String, String> channelMap = Arrays.stream(ChannelCodeEnum.values())
                .collect(Collectors.toMap(ChannelCodeEnum::getCode, ChannelCodeEnum::getName));

        List<MsgNotice> msgNoticeList = noticeService.listNoticeByIds(noticeIds);
        Map<Long, MsgNotice> noticeMap = msgNoticeList.stream().collect(Collectors.toMap(MsgNotice::getId, Function.identity()));

        List<TenantResp> tenantList = systemApi.listTenantByIds(tenantIds);
        Map<String, String> tenantMap = tenantList.stream().collect(Collectors.toMap(TenantResp::getId, TenantBase::getName));
        // 设置消息内容动态渲染
        List<HistoryQueryResp> respData = historyPage.getRecords().stream()
                .map(e -> {
                    HistoryQueryResp resp = historyAssembly.toMsgHistoryQueryResp(e);
                    MsgNotice msgNotice = noticeMap.get(e.getNoticeId());
                    if (Objects.nonNull(msgNotice)) {
                        resp.setNoticeCode(msgNotice.getCode());
                    }
                    resp.setChannelCode(e.getChannelCode());
                    resp.setChannelName(channelMap.get(e.getChannelCode()));
                    resp.setTenantName(tenantMap.get(e.getTenantId()));
                    return resp;
                }).collect(Collectors.toList());

        Page<HistoryQueryResp> resp = new Page<>(historyPage.getCurrent(), historyPage.getSize(), historyPage.getTotal());
        resp.setRecords(respData);
        return resp;
    }

    private HistoryQueryResp getMsgHistoryQueryResp(MsgHistory history, NoticeChannel noticeChannel) {
        HistoryQueryResp resp = historyAssembly.toMsgHistoryQueryResp(history);

        MsgNotice msgNotice = noticeService.getNotice(history.getNoticeId());
        if (Objects.isNull(msgNotice)) {
            resp.setMsgContent("生成发送内容失败, 消息通知ID " + history.getNoticeId() + " 不存在");
            return resp;
        }
        resp.setNoticeCode(msgNotice.getCode());

        MsgSettings msgSettings = JSON.parseObject(noticeChannel.getSettings(), MsgSettings.class);
        msgSettings.parseField(history.getMsgFields());
        msgSettings.evaluate();
        resp.setMsgContent(msgSettings.getRenderContent());

        ChannelCodeEnum channelCodeEnum = ChannelCodeEnum.ofByCode(history.getChannelCode());
        resp.setChannelName(Optional.ofNullable(channelCodeEnum).map(ChannelCodeEnum::getName).orElse("未知"));
        return resp;
    }


    /**
     * 根据主键查询 消息推送
     *
     * @param id 主键
     * @return 返回记录，没有返回null
     */
    @Override
    @FieldBindHandle
    public HistoryQueryResp getById(Long id) {
        MsgHistory history = historyManager.getById(id);
        Assert.notNull(history, "未查询到推送记录");

        NoticeChannel noticeChannel = channelService.getNoticeChannel(history.getChannelId());
        Assert.notNull(noticeChannel, "未查询到关联的通知渠道");

        return getMsgHistoryQueryResp(history, noticeChannel);
    }

    /**
     * 通过id删除 消息推送
     *
     * @param id 删除的主键
     * @return 返回影响行数
     */
    @Override
    public boolean deleteById(Long id) {
        return historyManager.removeById(id);
    }

    /**
     * 通过ids删除 消息推送
     *
     * @param ids 删除的主键
     * @return 返回影响行数
     */
    @Override
    public boolean deleteByIds(List<Long> ids) {
        return historyManager.removeByIds(ids);
    }

    @Override
    @FieldBindHandle
    @Tenant(level = DataIsolateLevel.MGT)
    public void rePush(Long id) throws Exception {
        MsgHistory history = historyManager.getById(id);
        Assert.notNull(history, "未查询到推送记录");

        MsgNotice msgNotice = noticeService.getNotice(history.getNoticeId());
        Assert.notNull(msgNotice, String.format("未查询 %s 消息通知", history.getNoticeId()));

        MsgPushParam msgPushParam = toMsgPushParam(history, msgNotice);
        pushProcess.push(msgPushParam, String.valueOf(id));
    }

    private MsgPushParam toMsgPushParam(MsgHistory history, MsgNotice msgNotice) throws Exception {
        MsgPushParam pushParam = new MsgPushParam();
        StopWatch stopWatch = StopWatch.createStarted();
        MsgParam msgParam = new MsgParam();
        List<MsgField> msgFieldList = history.getMsgFields();

        msgParam.addAllMsgField(msgFieldList).setPageUrl(history.getPageUrl())
                .setBizKey(history.getBizKey()).setBizType(history.getBizType())
                .setCode(msgNotice.getCode()).setToUser(history.getToUser());

        pushParam.setHistoryId(history.getId());
        pushParam.setMsgParam(msgParam);

        pushParam.setChannelType(history.getChannelType());
        log.info("toMsgPushReq time: {} ms", stopWatch.getTime(TimeUnit.MILLISECONDS));
        stopWatch.stop();
        return pushParam;
    }
}